<?php
declare (strict_types=1);

namespace app\course\logic\base;

use app\common\exception\LogicException;
use app\common\model\{Attach, ProductType};
use app\course\logic\classes\ClassesLink;
use app\course\logic\live\LiveSection;
use app\course\logic\record\LearnRecord;
use app\course\logic\video\VideoSection;
use app\course\model\base\{CourseSection as CourseSectionModel};
use app\course\model\live\CourseSectionLive;
use app\course\model\payment\PaymentOrderItem as POI;
use app\course\model\record\LearnCourseRecord;
use app\course\model\video\CourseSectionVideo;
use app\school\logic\school\School;
use app\user\logic\teacher\Teacher;
use app\user\logic\user\FaceVerify;
use ArrayObject;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Paginator;

/**
 * 课程课时数据
 * @method group(string $string)
 * @method whereCourseId($course_id)
 */
class CourseSection extends CourseSectionModel
{

    /**
     * 根据课程ID和课时PID获取章节课时
     * Author   亓官雨树<lucky.max@foxmail.com>
     * Date 2020-03-23
     *
     * @throws DbException
     */
    public function getCourseAppointSections($course_id = 0, $pid = 0, int $classId = 0): Paginator
    {
        // 章节信息
        $section = $this->scope(['status'])->with(['course_lock'])->where(['course_id' => $course_id, 'pid' => $pid])->order(['best_sort' => 'ASC', 'id' => 'ASC'])->paginate(static::getListRows());
        // 班级课锁、课程锁
        $classlock = ($classId && $classId != $course_id) ? app(Course::class)->where('id', $classId)->value('lock') : 0;
        /** @var FaceVerify $faceVerify */
        $faceVerify = app(FaceVerify::class);
        foreach ($section as $sec) {
            $has_child        = $this->where(['course_id' => $course_id, 'pid' => $sec['id']])->findOrEmpty();
            $sec->has_child   = $has_child->isEmpty() ? 0 : 1;
            $sec->product_key = [1 => 'video', 2 => 'live'][$sec->getAttr('course_type')];
            if ($sec->is_last) {
                // 购买情况
                $sec->is_buy = $this->isBuy($course_id, $sec->id);
                // 进度
                $sec->section_rate = $sec->course_type == 1 ? app(LearnRecord::class)->sectionRate($sec['id']) : null;
                // 用户解锁 // 跟随课程锁
                $lock      = ($classId && $classId != $course_id) ? $classlock : $sec->course_lock;
                $sec->lock = $sec->course_lock = $lock;
                if ($sec->course_type == 2) {
                    $sec->lock = 0;
                } else if (request()->mid && $lock) {
                    // 用户上一课时是否学完
                    $prevDone = $this->isPrevSecLearnFinished($course_id, $sec->id, $classId);
                    $prevDone && $sec->lock = 0;
                }
                // 解析课时信息
                if ($sec->course_type == 1) {
                    $sec->section_data = $this->parseCourseHour($sec);
                } else if ($sec->course_type == 2) {
                    $sec->section_live = $this->parseCourseHour($sec);
                }
                // 资料数据
                $sec->material_data = null;
                // 直播进度
                $sec->live_rate = $sec->course_type == 2 ? $this->parseLiveRate($sec) : null;
                // 带货课程
                // $sec->course_list = app(Course::class)->where('id', 'IN', $sec->course_ids)->field('id,title')->select();
                // 人脸认证数据
                $sec->face_data = $faceVerify->getFaceData('course_section', 2, intval($sec->id), request()->mid);
            }
        }

        return $section;
    }

    // 课时是否购买
    public function isBuy($course_id, $section_id, $mid = 0): int
    {
        $mid        = $mid ?: request()->mid;
        $course_buy = app(CourseStudent::class)->isBuy($course_id, $mid);
        if ($course_buy) return 1;

        $order_status = app(POI::class)->alias('i')->join('payment_order o', 'i.order_no=o.order_no')->where('i.product_id', $course_id)
            ->whereIn('i.product_type_id', Course::getCourseProducts())->where('i.user_id', $mid)->where('i.section_id', $section_id)->where('o.status', 'IN', '20,30,40')
            ->whereRaw('i.expire_time >' . time() . ' OR i.expire_time is null')->findOrEmpty();

        return $order_status->isEmpty() ? 0 : 1;
    }

    /**
     * 解析课时信息
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-05-08
     */
    public function parseCourseHour($sec)
    {
        $mid            = request()->mid;
        $type           = $sec->product_key;
        $course_data_id = $sec->getAttr('course_data_id');
        $classname      = '\\app\\course\\model\\' . $type . '\\CourseSection' . ucfirst($type);
        $section_data   = new ArrayObject();

        if ('video' == $type && class_exists($classname) && $course_data_id > 0) {
            $data = app($classname)->with(['attach'])->where('id', $course_data_id)->findOrEmpty()->append(['data_type_text']);
            if (!$data->isEmpty()) {
                if ($data->stock == 'bokecc') $data->fileurl = [];
                $section_data = $data;
                // $section_data->data_txt = $data->getAttr('data_txt_url');
            }
        } else if ('live' == $type && class_exists($classname)) {
            $data = app($classname)->where('room_no', $sec->id)->findOrEmpty();
            if (!$data->isEmpty()) {
                $data->data_type = 1;
                // 当前用户身份【当课讲师、其他】
                $data->identify = 'talker'; // 其他
                // if ( $data->live_type == 3 && $mid ) {
                // 课程属于讲师
                $teacher_id = app(Course::class)->getCourseTeacherUid($data->course_id);
                if ($teacher_id == $mid) $data->identify = 'presenter';// 当课讲师
                // }
                $section_data = $data;
            }
        }

        return $section_data;
    }

    /**
     * 解析直播进度
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-05-08
     */
    public function parseLiveRate($sec, $replay = true): array
    {
        if (!request()->mid) {
            return ['status' => 0, 'status_text' => '未登录'];
        }

        $live_data   = app(LiveSection::class)->where('room_no', $sec->id)->findOrEmpty();
        $close_time  = 0;
        $create_time = 0;
        if (!$live_data->isEmpty()) {
            $close_time  = $live_data->getAttr('close_time');
            $create_time = $live_data->getAttr('create_time');
        }

        $time      = time();
        $callback  = null;
        $recordid  = null;
        $replayurl = null;
        if (($live_data->isEmpty() && $sec->end_time < $time) || (!$live_data->isEmpty() && $close_time && $close_time < $time)) {
            $status = 992;
            $text   = '已结束';
            if ($replay) {
                if ($live_data->live_type == 2) {
                    $recordid = $live_data->cc_replay_id;
                } else if ($live_data->live_type == 3) {
                    $replayurl = $live_data->cc_replay_url;
                } else {
                    $callback = app(LiveSection::class)->getCallbackUrl($sec->id);
                }
            }
        } else if (!$create_time) {
            $status = 957;
            $text   = '未开始';
        } else {
            $status = 999;
            $text   = '直播中';
        }

        return $replay ? [
            'status'        => $status,
            'status_text'   => $text,
            'callback_url'  => $callback,
            'cc_replay_id'  => $recordid,
            'cc_replay_url' => $replayurl,
        ] : [
            'status'      => $status,
            'status_text' => $text
        ];
    }

    // 课时数据
    public function sectionInfo($section_id)
    {
        $info = $this->findOrEmpty($section_id);

        if ($info->isEmpty()) return $info;

        $info->product_key = ProductType::getProductKeyById(Course::getProductIdByCourseType($info->getAttr('course_type')));

        return $info;
    }

    // 第一个课时
    public function firstSectionHour($course_id)
    {
        $info = $this->where('course_id', $course_id)->where('is_last', 1)->where('status', 1)->findOrEmpty();

        if ($info->isEmpty()) return $info;

        $info->product_key  = ProductType::getProductKeyById(Course::getProductIdByCourseType($info->getAttr('course_type')));
        $info->section_data = $this->parseCourseHour($info);
        return $info;
    }

    /**
     * 获取课程第一个课时
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-10-12
     */
    public function getCourseCurrSecLast($course_id)
    {
        return $this->whereCourseId($course_id)->where('is_last', 1)->where('status', 1)->order('pid')->value('id');
    }

    /**
     * 订单页获取的产品信息
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date：   2020-05-08
     */
    public function getProductInfo($data)
    {
        $type_arr = [
            1 => 101, // 视频
            2 => 102, // 音频
            3 => 103, // 图文
            4 => 104, // 电子书
            // 5 => 105, // 试卷
            // 6 => 106, // 链接
        ];
        // 数据
        $field = 'id,title,price,course_id,course_type,course_data_id';
        $info  = $this->field($field)->with(['mhm_id'])->findOrEmpty($data->section_id);

        $info->product_key = ProductType::getProductKeyById(Course::getProductIdByCourseType($info->getAttr('course_type')));
        $course            = app(Course::class)->withTrashed()->where('id', $info->course_id)->with(['cover_url'])->field('cover')->findOrFail();
        $info->cover_url   = $course->cover_url;

        // 解析课时信息
        $section_data = $this->parseCourseHour($info);

        $section_data = isset($section_data->data_type) ? $section_data : null;

        if ($info->course_type == 1 && $section_data) {

            $info->type_id   = $type_arr[$section_data->data_type];
            $info->type_text = $section_data->data_type_text;

        } else if ($info->course_type == 2) {

            $info->type_id   = 101;
            $info->type_text = '视频';
        }

        $info->expire_time = $data->expire_time;
        $info->expire_rest = $data->expire_time ? ceil(max(0, $data->expire_time - time()) / 86400) : -1;

        return $info;
    }

    // 讲师获取章节树

    /**
     * @throws ModelNotFoundException
     * @throws DbException
     * @throws DataNotFoundException
     */
    public function getCourseSectionTree($course_id, $pid = 0, $sec_level = null)
    {
        if (null === $sec_level) {
            $sec_level = app(Course::class)->where('id', $course_id)->value('section_level');
            $sec_level = $sec_level ?: 0;
        }

        return $this->field('id,pid,title,level,is_last,status,course_type,live_provider,start_time,end_time')
            ->where([
                'course_id' => $course_id,
                'pid'       => $pid
            ])->select()->each(function ($sec) use ($course_id, $sec_level) {

                if ($sec->course_type == 2) {
                    $course                = app(Course::class)->findOrFail($course_id);
                    $sec->course_live_type = $course->limit_buy_count == 1 ? 3 : $course->course_live_type;
                }

                $sec->course_sec_level = $sec_level;

                if (!$sec->is_last) {
                    $sec->child = $this->getCourseSectionTree($course_id, $sec->id, $sec_level);
                } else {
                    if ($sec->course_type == 2) {

                        $time       = time();
                        $live_data  = app(LiveSection::class)->where('room_no', $sec->id)->findOrEmpty();
                        $close_time = $create_time = 0;

                        if (!$live_data->isEmpty()) {
                            $close_time  = $live_data->getAttr('close_time');
                            $create_time = $live_data->getAttr('create_time');
                        }

                        if (($live_data->isEmpty() && $sec->end_time < $time) || (!$live_data->isEmpty() && $close_time && $close_time < $time)) {
                            $status = 992;
                            $text   = '已结束';
                            // } elseif ($sec->start_time < $time + 15*60 && $sec->start_time > $time ) {
                            //     $status   = 925;
                            //     $text     = '即将直播';
                        } else if (!$create_time) {
                            $status = 957;
                            $text   = '未开始';
                        } else {
                            $status = 999;
                            $text   = '直播中';
                        }

                        $sec->live_info = [
                            'status'      => $status,
                            'status_text' => $text,
                        ];
                        $sec->live_data = $live_data->isEmpty() ? new ArrayObject() : $live_data;
                    }
                }
            });
    }

    // 讲师获取课程目录层级
    public function getCourseSectionLevel($course_id): int
    {
        $sec_level = app(Course::class)->where('id', $course_id)->value('section_level');
        return $sec_level ?: 0;
    }

    // 根据讲师身份获取课时需要审核的状态
    public function getHourAuditStatus($teacher_user_id = 0): int
    {
        $user_id       = $teacher_user_id ?: request()->mid;
        $teacher       = app(Teacher::class)->where('user_id', $user_id)->findOrEmpty();
        $mhm_id        = $teacher->mhm_id;
        $teacher_audit = $teacher->is_audit;
        // 状态【1：通过；2：机构审核；3：平台审核；】
        if ($mhm_id == 1) {
            // 平台讲师
            return $teacher_audit ? 3 : 1;
        } else {
            // 非平台讲师
            $school_audit = app(School::class)->where('id', $mhm_id)->value('is_audit');
            return $teacher_audit ? 2 : ($school_audit ? 3 : 1);
        }
    }

    // 讲师添加章节
    public function addCourseSectionMenu($course_id, $pid, $title)
    {
        $param['title']     = $title;
        $param['pid']       = $pid;
        $param['status']    = 1;
        $param['course_id'] = $course_id;

        if ($pid == 0) {
            $course               = app(Course::class)->findOrFail($course_id);
            $param['course_type'] = $course->course_type;
            $param['level']       = 1;

        } else {
            $data = $this->findOrFail($pid);
            if ($course_id != $data->course_id) {
                throw new LogicException('课程与课时不匹配');
            }
            $param['course_type'] = $data->course_type;
            $param['level']       = $data->level + 1;
        }

        $param['course_type'] == 2 && $param['live_provider'] = app(LiveSection::class)->checkLiveProvider();

        $add = $this::create($param);
        if ($add->isEmpty()) {
            throw new LogicException('添加失败');
        }
        return $add;
    }

    // 添加课时
    public function addCourseSectionData($course_id, $pid, $param)
    {
        // 课程类型
        $course_type = app(Course::class)->where('id', $course_id)->value('course_type');
        switch ($course_type) {
            case 2:
                $param['live_provider'] = app(LiveSection::class)->checkLiveProvider();
                $re                     = app(LiveSection::class)->addHour($course_id, $pid, $param);
                break;

            case 1:
            default:
                $re = app(VideoSection::class)->addHour($course_id, $pid, $param);
                break;
        }

        return $re;
    }

    // 编辑章节目录
    public function editCourseSectionMenu($course_id, $sid, $title)
    {
        $data = $this->findOrFail($sid);
        if ($course_id != $data->course_id) {
            throw new LogicException('课程与课时不匹配');
        }

        $data->title = $title;
        if (!$data->save()) {
            throw new LogicException('编辑失败');
        }

        return $data;
    }

    // 编辑课时时获取原数据

    /**
     * @throws DataNotFoundException
     * @throws ModelNotFoundException
     * @throws DbException
     */
    public function getCourseSectionData($course_id, $sid)
    {

        $data = $this->findOrFail($sid);
        if ($course_id != $data->course_id) {
            throw new LogicException('课程与课时不匹配');
        }

        // 参数
        if (1 == $data->course_type) {
            // 课时数据
            $data->section_data = app(CourseSectionVideo::class)->findOrFail($data->course_data_id)->append(['data_type_text', 'attach_base']);
            // 资料数据
            $data->material_data = null;
        } else if (2 == $data->course_type) {

            $live_data = app(CourseSectionLive::class)->where('room_no', $sid)->findOrEmpty();

            if ($live_data->isEmpty()) {
                $data->playback_data = null;
                $data->is_end        = $data->end_time < time() ? 1 : 0;
            } else {
                $data->playback_data = $live_data->attach_id ? $this->getMaterialData($live_data->attach_id) : null;
                $data->is_end        = $live_data->close_time ? 1 : 0;
            }
        }

        return $data;
    }

    /**
     * @throws ModelNotFoundException
     * @throws DataNotFoundException
     * @throws DbException
     */
    public function getMaterialData($attach_id): ?array
    {
        $data = app(Attach::class)->find($attach_id);

        return $data ? [
            'attach_id' => $data->id,
            'savename'  => $data->savename,
            'fileurl'   => $data->fileurl,
            'stock'     => $data->stock,
        ] : null;
    }

    // 讲师编辑课时
    public function editCourseSectionData($course_id, $sid, $param)
    {
        // 课程类型
        $course_type = app(Course::class)->where('id', $course_id)->value('course_type');
        switch ($course_type) {
            case 1:
                $re = app(VideoSection::class)->editHour($course_id, $sid, $param);
                break;

            case 2:
            default:
                $re = app(LiveSection::class)->editHour($course_id, $sid, $param);
                break;
        }

        return $re;
    }

    // 课时数量
    public function getCourseSectionCount($course_id): int
    {
        // 课程类型
        $course_type = $this->where('course_id', $course_id)->value('course_type');
        if ($course_type == 4) {
            // 课程IDs
            $ids = app(ClassesLink::class)->getCourseIds($course_id);

            // 班级课时
            return $this->where('course_id', 'IN', $ids)->where('is_last', 1)->count();
        } else {
            return $this->where('course_id', $course_id)->where('is_last', 1)->count();
        }
    }

    // 讲师删除课程
    public function deleteCourseSection($course_id, $sid): bool
    {
        $data = $this->findOrFail($sid);
        if ($course_id != $data->course_id) throw new LogicException('课程与课时不匹配');

        if (!$data->delete()) throw new LogicException('删除失败');

        return true;
    }

    public function getSectionTitle($id)
    {
        return $this->where('id', $id)->value('title') ?: null;
    }

    /**
     * 查询上一课时是否完成学习，包含班级课【班级课跳过直播课】
     * Author: 亓官雨树 <lucky.max@foxmail.com>
     * Date: 22/12/29
     *
     * @param     $courseId
     * @param     $secId
     * @param int $classId
     * @return bool
     */
    public function isPrevSecLearnFinished($courseId, $secId, int $classId): bool
    {
        // 获取上一课程、课时
        $prev = $this->getPrevSecId($courseId, $secId, $classId);
        if (!count($prev)) return true;
        $prevSecId    = $prev['secId'];
        $prevCourseId = $prev['courseId'];
        //
        $finished = app(LearnCourseRecord::class)->where('user_id', request()->mid)->where('course_id', $prevCourseId)->where('section_id', $prevSecId)->where('finished', 1)->findOrEmpty();
        return !$finished->isEmpty();
    }

    /**
     * 获取上一课时ID，包含班级课【班级课跳过直播课】
     * Author: 亓官雨树 <lucky.max@foxmail.com>
     * Date: 22/12/29
     *
     * @param     $courseId
     * @param     $secId
     * @param int $classId
     * @return array
     */
    private function getPrevSecId($courseId, $secId, int $classId): array
    {
        // 同级课时ID集
        $ids = $this->where('course_id', $courseId)->where('is_last', 1)->where('status', 1)->order(['pid', 'best_sort', 'id'])->column('id');
        // 当前课时同级目录指针
        $needle = array_search($secId, $ids);
        if ($needle) return ['courseId' => $courseId, 'secId' => $ids[$needle - 1]];
        // 检查班级课上一课程
        if ($classId && $classId != $courseId) {
            // $courses = app(ClassesLink::class)->withJoin(['course'])->where('classes_id', $classId)->select()->toArray();
            // if (!count($courses)) return [];
            // $courseIds = array_column($courses, 'course_id');
            $courseIds = app(ClassesLink::class)->withJoin(['course'])->where('course.course_type', 1)->where('classes_id', $classId)->column('course_id');
            if (!$courseIds || (is_array($courseIds) && !count($courseIds))) return [];
            $needle = array_search($courseId, $courseIds);
            if (!$needle) return [];
            $prevCourseId = $courseIds[$needle - 1];
            // 上一课程最后一个课时
            $id = $this->where('course_id', $prevCourseId)->where('is_last', 1)->where('status', 1)->order(['pid' => 'DESC', 'best_sort' => 'DESC', 'id' => 'DESC'])->value('id');
            return ['courseId' => $prevCourseId, 'secId' => intval($id)];
        }
        return [];
    }
}
